﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

#include <list>
#include <set>

#include "UnitHelper.h"
#include "OwnerPtr.h"

#ifdef _MSC_VER
#ifdef EXPRENGINE_EXPORTS
#define EXPR_ENGINE_API __declspec(dllexport)
#else
#define EXPR_ENGINE_API __declspec(dllimport)
#endif
#else
#define EXPR_ENGINE_API
#endif

/// \brief 表达式引擎接口单元
namespace gcmp
{
    /// \brief 表达式计算结果数据类型
    enum class ExprDataType : int32_t
    {
        Unknown = 0, ///< 未知
        Integer = 1, ///< 整数
        Boolean = 2, ///< 布尔类型
        Float = 3, ///< 浮点类型
        String = 4 ///< 字符串类型
    };

    /// \brief 表达式节点类型
    enum class ExprType : int32_t
    {
        UnaryOperate = 0, ///< 一元操作符，如-（负）等
        BinaryOperate = 1, ///< 二元操作符，如+、-、*、/等
        Const = 2, ///< 常量，如100,'宽度'等
        ID = 3, ///< 标识符，唯一识别的变量，如Name
        Expr = 4, ///< 嵌套的表达式节点
        Function = 5 ///< 自定义函数
    };

    const int MAX_STRING_VALUE_LENGTH = 16;

    /// \brief 表达式节点数据对象
    class EXPR_ENGINE_API ExprNodeData
    {
    public:
        /// \brief 析构函数
        virtual ~ExprNodeData() = default;

        /// \brief 获得数据值类型
        /// 
        /// \return 数据类型。如果表达式非法，返回Unknown。
        virtual ExprDataType GetType() const = 0;

        /// \brief 获得单位
        /// 
        /// \return 单位
        virtual UnitMap GetUnitType() = 0;

        /// \brief 设置单位
        /// 
        /// \param unitType 单位
        virtual void SetUnitType(const UnitMap& unitType) = 0;

        /// \brief 设置整型值
        /// 
        /// \param aValue 整型值
        virtual void SetAsInteger(long aValue) = 0;

        /// \brief 得到整型值
        /// 
        /// \return 整型值
        virtual long GetAsInteger() const = 0;

        /// \brief 设置布尔值
        ///
        /// \param aValue 布尔值
        virtual void SetAsBoolean(bool aValue) = 0;

        /// \brief 得到布尔值
        /// 
        /// \return 布尔值
        virtual bool GetAsBoolean() const = 0;

        /// \brief 设置浮点型值
        ///
        /// \param aValue 浮点值
        virtual void SetAsFloat(double aValue) = 0;

        /// \brief 得到浮点值
        /// 
        /// \return 浮点值
        virtual double GetAsFloat() const = 0;

        /// \brief 设置字符串值
        ///
        /// \param aValue 字符串值
        virtual void SetAsString(const wchar_t* aValue) = 0;

        /// \brief 得到字符串值
        ///
        /// \return 字符串值
        virtual const wchar_t* GetAsString() const = 0;
    };

    /// \brief 表达式语法树节点类
    class EXPR_ENGINE_API ExprNode
    {
    public:
        /// \brief 析构函数
        virtual ~ExprNode() {}

        /// \brief 得到节点类型
        /// 
        /// \return 节点类型
        virtual ExprType GetNodeType() const = 0;

        /// \brief 得到节点数据
        ///
        /// \return 节点数据
        virtual ExprNodeData* GetData() = 0;

        /// \brief 得到数据值类型
        ///
        /// \return 数据值类型
        virtual ExprDataType GetDataType() const = 0;

        /// \brief 得到数据值单位
        ///
        /// \return 单位
        virtual UnitMap GetUnitType() = 0;

        /// \brief 设置节点标记，用于记录额外信息
        ///
        /// \param aTag 节点标记
        virtual void SetTag(void* aTag) = 0;

        /// \brief 得到节点标记
        ///
        /// \return 节点标记
        virtual void* GetTag() const = 0;
    };

    /// \brief 访问表达式的节点
    class EXPR_ENGINE_API ExprNodeVisitor
    {
    public:
        virtual void Visit(const ExprNode* pNode) = 0;
    };

    /// \brief 函数参数列表
    class EXPR_ENGINE_API FunctionParams
    {
    public:
        virtual ~FunctionParams() {};

        /// \brief 添加参数，表达式引擎内部使用
        ///
        /// \param aParam 节点
        /// \return 节点
        virtual ExprNode* AppendParams(gcmp::OwnerPtr<ExprNode> aParam) = 0;

        /// \brief 得到参数个数
        ///
        /// \return 参数个数
        virtual short Count() const = 0;

        /// \brief 根据索引值获得参数
        ///
        /// \param aIndex 索引
        /// \return 参数值，对于非法索引返回nullptr
        virtual ExprNodeData* GetParam(short aIndex) = 0;
    };

    /// \brief 使用者需要继承此类提供表达式求值过程中的必要信息
    class EXPR_ENGINE_API IExprNodeDataProvider
    {
    public:
        virtual ~IExprNodeDataProvider() {}

        /// \brief 设置节点值类型
        ///
        /// \param [in] name 节点的Code
        /// \return 节点值类型
        virtual ExprDataType GetResultDataType(const wchar_t* name) = 0;

        /// \brief 获得节点数值单位
        ///
        /// \param [in] name 节点的Code
        /// \return 节点单位
        virtual ExprUnitType GetUnitType(const wchar_t* name) = 0;

        /// \brief 设置节点数据
        ///
        /// \param [in] name 节点的Code
        /// \param [out] data 用户通过data设置节点的值
        /// \param [out] aTag 节点标记
        virtual void SetExprData(const wchar_t* name, ExprNodeData* data, void* aTag = nullptr) = 0;

        virtual void ListExprData(std::list<std::wstring>& names) {};
    };

    /// \brief 自定义函数的基类
    class EXPR_ENGINE_API IExprCustomFunction
    {
    public:
        virtual ~IExprCustomFunction() {}

        /// \brief 函数名称
        ///
        /// \return 函数名称
        virtual const wchar_t* GetFunctionName() = 0;

        /// \brief 函数返回值类型
        ///
        /// \return 函数返回值类型
        virtual ExprDataType GetResultDataType() = 0;

        /// \brief 执行函数
        ///
        /// \param [in] params 函数参数
        /// \param [out] resultData 节点值，通过resultData设置函数值
        /// \param [out] aTag 节点标记
        /// \return 执行成功返回true，否则返回false
        virtual bool DoExectute(FunctionParams* params, ExprNodeData* resultData, void* aTag = nullptr) = 0;
    };

    /// \brief 错误类型
    enum class EXPR_ENGINE_API EnErrorType : int32_t
    {
        Undefine = 0, ///< 未定义的错误类型
        LParenAfterParam = 1, ///< 某个变量或数字后是一个左括号
        RedundantRParen = 2, ///< 右括号前面没有对应的左括号
        LastBinary = 3, ///< 最后一个字符是二目操作符
        NullExprInParens = 4, ///< 括号对里内容为空
        DivisorIsZero = 5, ///< 除数或者模数为0
        UnitInvalid = 6 ///< 单位计算无效
    };

    class EXPR_ENGINE_API IExprEngine;

    /// \brief 表达式解析器
    class EXPR_ENGINE_API IExprParser
    {
    public:
        virtual ~IExprParser() {}

        virtual const IExprEngine* GetEngine() const = 0;

        /// \brief 设置表达式
        ///
        /// \param aValue 表达式
        /// \return 返回把函数名转为内置大小写格式后的表达式字符串
        /// \note 设置表达式并不会进行计算
        virtual wchar_t* SetExpression(const wchar_t* aValue) = 0;

        /// \brief 计算表达式
        ///
        /// \note 调用此方法后才能够取得表达式的值
        virtual void Evaluate() = 0;

        /// \brief 获取表达式的返回值类型
        ///
        /// \return 返回值类型。如果表达式非法，将返回Unknown。
        virtual ExprDataType ResultType() const = 0;

        /// \brief 获取表达式的返回单位
        ///
        /// \return 返回单位
        virtual UnitMap ResultUnitType() = 0;

        /// \brief 获取表达式的返回值
        ///
        /// \return 返回值。如果表达式非法，将返回nullptr
        virtual ExprNodeData* Result() = 0;

        /// \brief 获取表达式是否为空
        ///
        /// \return 是否为空
        virtual bool GetIsNull() const = 0;

        /// \brief 获取表达式是否合法
        ///
        /// \return 是否合法
        virtual bool GetIsValid() const = 0;

        /// \brief 获取表达式错误位置
        ///
        /// \return 错误位置
        virtual int GetErrorPos() const = 0;

        /// \brief 获取表达式错误类型
        ///
        /// \return 错误类型
        virtual EnErrorType GetErrorType() const = 0;

        /// \brief 访问所有的节点
        ///
        /// \param pVisitor 访问器
        virtual void Accept(ExprNodeVisitor* pVisitor) const = 0;

        /// \brief 是否循环引用了某个标识符
        ///
        /// \param tokenName 标识符名字
        /// \return 是返回True，否返回false
        virtual bool IsCircularReferenced(const wchar_t* tokenName) const = 0;

        /// \brief 获取表达式引用的所有参数名
        ///
        /// \param [out] refParamNames 表达式引用的所有参数名列表
        /// \note 列表中只包括该表达式直接引用的参数名
        virtual void GetAllReferencedParamNames(std::set<const wchar_t*>& refParamNames) const = 0;

        virtual const ExprNode* GetRootNode() const = 0;

        virtual std::vector<const wchar_t*> GetExprNodeNames() const = 0;

        virtual void GetIDNodePositions(const std::wstring& nodeName, std::vector<std::pair<int, int>>& result) const = 0;
    };

    /// \brief 表达式引擎
    class EXPR_ENGINE_API IExprEngine
    {
    public:
        virtual ~IExprEngine() {}

        virtual gcmp::OwnerPtr<IExprParser> CreateParser() = 0;

        /// \brief 设置节点数据提供者对象
        ///
        /// \param pDataProvider 节点数据提供者
        virtual void SetNodeDataProvider(gcmp::OwnerPtr<IExprNodeDataProvider> pDataProvider) = 0;

        /// \brief 获取节点数据提供者对象
        ///
        /// \return 数据供应器指针
        virtual IExprNodeDataProvider* GetNodeDataProvider() = 0;

        /// \brief 注册自定义函数
        ///
        /// \param aFunction 用户自定义函数，由用户创建并管理
        virtual void RegisterFunction(IExprCustomFunction* aFunction) = 0;

        /// \brief 反注册自定义函数
        ///
        /// \param aFunction 用户自定义函数，由用户创建并管理
        virtual void UnRegisterFunction(IExprCustomFunction* aFunction) = 0;

        /// \brief 查找函数
        ///
        /// \param aName 函数名称
        /// \return 自定义函数，如果没有找到返回nullptr
        virtual IExprCustomFunction* FindFunction(const wchar_t* aName) = 0;

        /// \brief 查找函数并调整函数名的大小写
        ///
        /// \param [in/out] aName 函数名称，大小写不敏感
        /// \return 自定义函数。如果没有找到返回nullptr，且不会调整函数名；如果找到了，会把函数名调整为内置的大小写格式。
        virtual IExprCustomFunction* FindFunctionAndAdjustName(wchar_t* aName) = 0;

        /// \brief 注册表达式
        ///
        /// \param pExprParserName 表达式变量名称
        /// \param opNewExprParser 表达式对象指针
        /// \return true，注册成功；false，注册失败
        virtual bool RegisterExprParser(const wchar_t* pExprParserName, gcmp::OwnerPtr<IExprParser> opNewExprParser) = 0;

        /// \brief 反注册表达式
        ///
        /// \param pExprParserName 表达式变量名称
        /// \return true，反注册成功；false，反注册失败
        virtual bool UnRegisterExprParser(const wchar_t* pExprParserName) = 0;

        /// \brief 根据唯一的名称搜索已经注册的表达式对象
        ///
        /// \param pExprParserName 表达式变量名称
        /// \return 表达式对象指针
        virtual IExprParser* FindParser(const wchar_t* pExprParserName) = 0;
    };

    /// \brief 创建表达式引擎
    ///
    /// \return 创建的表达式引擎
    EXPR_ENGINE_API gcmp::OwnerPtr<IExprEngine> CreateEngine();
}
